package com.baoteng.rbspy.jobs;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.util.StringUtil;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoAlertPresentException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.ie.InternetExplorerOptions;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.baoteng.rbspy.config.car.RbSysConfig;
import com.baoteng.rbspy.service.OkHttpClientService;

@Component
public class LoginHolder {

	private static final Logger logger = LoggerFactory.getLogger(LoginHolder.class);

	@Autowired
	RedisTemplate<String, String> stringRedisTemplate;

	@Autowired
	RbSysConfig rbSysConfig;
	
	@Autowired
	OkHttpClientService okHttpClientService;

	private Process process;
	
	private Pattern pattern ;
	
	private String windowsPingArgument = " -n 5 -w 200";
	
	private String unixPingArgument = " -c 5 -w 5";
	
	private String pingCmd ;
	
	private String currentOS = System.getProperty("os.name");
	
	private WebDriver driver;
	
	private InternetExplorerOptions options ;
	
	/**
	 * 最大尝试次数
	 */
	static final int MAX_RETRY_ATTEMPTS = 1;

	@PostConstruct
	private void init() {
		if (currentOS.toLowerCase().contains("windows")) {
			 pingCmd = "ping "+rbSysConfig.getPingHost()+windowsPingArgument;
			 pattern = Pattern.compile(".*=(\\d+)ms.*ttl=(\\d+)", Pattern.CASE_INSENSITIVE);
		}else {
			 pingCmd = "ping "+rbSysConfig.getPingHost()+unixPingArgument;
			 pattern = Pattern.compile(".*ttl=(\\d+).*time=(\\d+[\\.]?[\\d+]*).*ms", Pattern.CASE_INSENSITIVE);
		}
		logger.info("config loginVpn is {}",rbSysConfig.isLoginVpn());
		if(rbSysConfig.isLoginVpn()) {
			DesiredCapabilities cap = DesiredCapabilities.internetExplorer();
			cap.setCapability(InternetExplorerDriver.ENABLE_PERSISTENT_HOVERING, false);
			cap.setCapability(InternetExplorerDriver.IE_ENSURE_CLEAN_SESSION, true);
			cap.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
			cap.setCapability(InternetExplorerDriver.NATIVE_EVENTS, false);
			cap.setCapability(InternetExplorerDriver.IGNORE_ZOOM_SETTING, true);
			cap.setCapability("newCommandTimeout", 360);
			cap.setCapability(InternetExplorerDriver.LOG_LEVEL, "DEBUG");
			cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
			System.setProperty("webdriver.ie.driver", rbSysConfig.getIeDriverPath());
			options = new InternetExplorerOptions(cap);
			try {
				driver = new InternetExplorerDriver(options);
				if(checkVpnOnline()) {
					vpnLogout(driver);
				}
				vpnLogin();
			} catch (Exception e) {
				logger.error("ie WebDriver init error.",e);
			}
		}
	}
	
	@PreDestroy
	private void destory(){
		try {
			if(driver!=null) {
//				driver.getWindowHandles().stream().forEach(w->{
//					driver.switchTo().window(w);
//					if(driver.getTitle().contains("人保") && driver.getTitle().contains("VPN系统")) {
//						logger.info("vpn 在线保留窗口不关闭。");
//					}else {
//						driver.close();
//					}
//				});
				driver.quit();
			}
			
			if(process!=null && process.isAlive()) process.destroyForcibly();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public boolean checkVpnOnline() {
		try {
			process = Runtime.getRuntime().exec(pingCmd);
			long begin = System.currentTimeMillis();
			if (!process.waitFor(10, TimeUnit.SECONDS)) {
				process.destroyForcibly();
			}
			logger.info("ping cmd cost:"+(System.currentTimeMillis()-begin));
			int doneCounter =0;
			if (process.exitValue() == 0 || process.exitValue() == 1) {
				try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"))) {
					String line = null;
					while ((line = reader.readLine()) != null) {
						logger.info("ping command line echo:{}",line);
						Matcher matcher = pattern.matcher(line);
						doneCounter += matcher.find()?1:0;
					}
				}
			}
			return doneCounter >=3 ;
		} catch (Exception e) {
			logger.error("checkVpnOnline error.",e);
		}
		return false;
	}
	
	private void findActiveVpnWindow(WebDriver driver,InternetExplorerOptions options) {
		boolean openWindows = true;
		if(driver!=null && driver.getWindowHandles()!=null && driver.getWindowHandles().size()>0) {
			String windowId = driver.getWindowHandle();
			for (int i = 0; i < 5; i++) {
				try {
					Thread.sleep(100);
					Alert alert = driver.switchTo().alert();
					String alertText = alert.getText();
					if (StringUtil.isNotBlank(alertText)) {
						logger.info(" logout alert:{}", alert.getText());
						if (alert.getText().contains("确定重新登录")) {
							alert.dismiss();
						} else {
							alert.accept();
						}
					}
				} catch (NoAlertPresentException e) {
					logger.error("no alert.");
					break;
				}catch (InterruptedException e1) {
					logger.error("sleep error.",e1);
					break;
				}
			}
			for(String _wid:driver.getWindowHandles()) {
				if(StringUtils.isNotBlank(_wid) && _wid.indexOf("error")==-1) {
					openWindows = false;
				}else {
					continue;
				}
				if(!_wid.equals(windowId)) {
					driver.switchTo().window(_wid);
				}
				vpnLogout(driver);
			}
		}
		if(openWindows) {
			driver = new InternetExplorerDriver(options);
		}
	}
	
	private boolean vpnPwdInput(String pwd) {
		try {
			String cmd = rbSysConfig.getPwdInputpath()+" "+pwd;
			Process process = Runtime.getRuntime().exec(cmd);
			long begin = System.currentTimeMillis();
			if (!process.waitFor(10, TimeUnit.SECONDS)) {
				process.destroyForcibly();
			}
			logger.info("cost:{}",System.currentTimeMillis()-begin);
			if (process.exitValue() == 0 || process.exitValue() == 1) {
				return true;
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			throw new RuntimeException(e);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	private void vpnLogout(WebDriver driver) {
			logger.info("user logon.");
			driver.navigate().to(rbSysConfig.getVpnLogouturl());
			try {
				
				for (int i = 0; i < 5; i++) {
					Thread.sleep(500);
					try {
						Alert alert = driver.switchTo().alert();
						String alertText = alert.getText();
						if (StringUtil.isNotBlank(alertText) && 
								(alertText.contains("关闭窗口")||alertText.contains("关闭此窗口")||alertText.contains("关闭选项卡")||alertText.contains("关闭此选项卡"))
								) {
							logger.info(" logout alert:{}",alert.getText());
							alert.dismiss();
						}else {
							logger.info(" logout alert:{}",alert.getText());
							alert.accept();
						}
					} catch (NoAlertPresentException e) {
						logger.error("vpnLogout NoAlertPresentException.",e);
						break;
					}
				}
			} catch (Exception e) {
				logger.error("vpnLogout error.",e);
			}
			try {
				if(driver.getTitle().contains("证书错误")) {
					driver.get("javascript:document.getElementById('overridelink').click();");
				}
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			try {
				
				for (int i = 0; i < 5; i++) {
					Thread.sleep(500);
					try {
						Alert alert = driver.switchTo().alert();
						String alertText = alert.getText();
						if (StringUtil.isNotBlank(alertText) && (alertText.contains("关闭窗口")||alertText.contains("关闭此窗口")||alertText.contains("关闭选项卡")||alertText.contains("关闭此选项卡"))
								) {
							logger.info(" logout alert:{}",alert.getText());
							alert.dismiss();
						}else {
							logger.info(" logout alert:{}",alert.getText());
							alert.accept();
						}
					} catch (NoAlertPresentException e) {
						logger.error("vpnLogout NoAlertPresentException.",e);
						break;
					}
				}
			} catch (Exception e) {
				logger.error("vpnLogout error.",e);
			}
	}
	
	public boolean vpnLogin() {
		try {
			findActiveVpnWindow(driver,options);
			String windowId = driver.getWindowHandle();
			driver.switchTo().window(windowId);
			driver.get(rbSysConfig.getVpnUrl());
			for (int i = 0; i < 5; i++) {
				Thread.sleep(100);
				try {
					Alert alert = driver.switchTo().alert();
					String alertText = alert.getText();
					if (StringUtil.isNotBlank(alertText) && (alertText.contains("关闭窗口")||alertText.contains("关闭此窗口")||alertText.contains("关闭选项卡")||alertText.contains("关闭此选项卡"))
							) {
						logger.info(" logout alert:{}",alert.getText());
						alert.dismiss();
					}else {
						logger.info(" logout alert:{}",alert.getText());
						alert.accept();
					}
				} catch (NoAlertPresentException e) {
					logger.error("vpnLogout NoAlertPresentException.",e);
					break;
				}
			}
			final JavascriptExecutor jsDriver =  (JavascriptExecutor)driver;
			if(driver.getTitle().contains("证书错误")) {
				driver.get("javascript:document.getElementById('overridelink').click();");
			}
			checkPageIsReady(driver, 5);
			List<WebElement> _elements = driver.findElements(By.tagName("a"));
			if (_elements != null) {
				_elements.stream().filter(e -> {
					String hrefUrl = e.getAttribute("href");
					if (hrefUrl.contains("chosekey")) {
						jsDriver.executeScript("arguments[0].click();", e);
						return true;
					}
					return false;
				}).findFirst();
			}
			checkPageIsReady(driver, 5);
			WebElement element = driver.findElement(By.xpath("//input[@value='开始认证']"));
			if (element != null) {
				jsDriver.executeScript("arguments[0].click();", element);
				for (int i = 0; i < 5; i++) {
					Thread.sleep(200);
					try {
						Alert alert = driver.switchTo().alert();
						String alertText = alert.getText();
						logger.info(" login alert:{}",alert.getText());
						if (StringUtil.isNotBlank(alertText) && alertText.contains("请输入用户PIN码")) {
							vpnPwdInput(rbSysConfig.getVpnPwd());
							alert.accept();
							break;
						} else {
							alert.accept();
						}
					} catch (NoAlertPresentException e) {
						logger.error("vpn pwd input error.",e);
					}
				}
			}
			driver.switchTo().window(windowId);
			checkPageIsReady(driver, 5);
			if(checkVpnOnline()) return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 每隔一秒check一下页面加载是否完成
	 */
	private static void checkPageIsReady(WebDriver driver, int retrycount) {
		JavascriptExecutor js = (JavascriptExecutor) driver;
		for (int i = 0; i < retrycount; i++) {
			if ("complete".equals(js.executeScript("return document.readyState;").toString())) {
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 重连次数
	 */
//	private int retryAttempts = 0;

//	@Scheduled(fixedDelay = 1 * 60 * 1000)
//	public void vpnMonitor() {
//		if(!checkVpnOnline()) {
//			vpnLogin();
//		}
//	}
	
	@Scheduled(fixedDelay = 1 * 60 * 1000)
	public void sessionRefresh() {
		logger.info("LoginHolder sessionRefresh start.");
		
		if(this.rbSysConfig.isLoginVpn() && !checkVpnOnline()) {
			logger.info("vpn offline.");
			boolean relogin = vpnLogin();
			logger.info("vpn relogin status:{}",relogin);
		}else {
			boolean isLogin = false;
			if (!okHttpClientService.isOnline()) {
				isLogin= okHttpClientService.login();
			}
			okHttpClientService.carlogin();
			okHttpClientService.cbclogin();
			okHttpClientService.ciflogin();
			logger.info("sessionRefresh status:{}",isLogin);
		}
		logger.info("sessionRefresh end.");
	}

	@Scheduled(cron = "0 7 0 * * ?")
	public void reLogin() {
		logger.info("LoginHolder reLogin start.");
		okHttpClientService.logout();
		if (okHttpClientService.login()) {
			okHttpClientService.carlogin();
			okHttpClientService.ciflogin();
			okHttpClientService.cbclogin();
		}
		logger.info("reLogin end.login status is {}",okHttpClientService.isOnline());
	}
}
